﻿/*************************************************************************************
 * 文 件 名:   HK_HierarchyPro
 * 
 * 描    述:  本工具用于增强 Hierarchy 面板的显示功能。通过本工具，开发者可以更直观地查看游戏对象的状态、层级和组件信息，有效地提高工作效率。
 * 
 * 版    本：  V1.2
 * 创 建 者：  京产肠饭
 * 创建时间：  2021/11/29
 * 链   接：  https://gitee.com/JingChanChangFan/hk_-unity-tools
*************************************************************************************/

using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEditor;
using System;

namespace HKTools
{
    [InitializeOnLoad]
    public class HK_HierarchyPro
    {
        public static void ChangeState(bool isActive)
        {
            if (isActive == true)
            {
                EditorApplication.hierarchyWindowItemOnGUI += OnHierarchyWindowItemOnGUI;
            }
            else
            {
                EditorApplication.hierarchyWindowItemOnGUI -= OnHierarchyWindowItemOnGUI;
            }

            EditorApplication.RepaintHierarchyWindow();
        }

        // 用于记录哪些 GameObject 已经被标记
        static Dictionary<int, bool> markedObjects = new Dictionary<int, bool>();

        // 最大显示的图标数量
        const int MaxIconCount = 3;

        static Transform curOffsetTF = null;
        static int offsetCount = 0;

        // 想要显示的 组件 列表（哪个报错，移除哪个）
        static List<Type> showTypes = new List<Type>()
        {
            typeof(Camera),
            typeof(Collider),
            typeof(AudioSource),
            typeof(AudioListener),
            typeof(HK_AreaTarget),
            typeof(HK_FollowTarget),
            typeof(HK_RotTarget),
            typeof(HK_TargetCamera),
            typeof(HK_TipsCtrl)
        };

        static void OnHierarchyWindowItemOnGUI(int instanceID, Rect selectionRect)
        {
            GameObject go = EditorUtility.InstanceIDToObject(instanceID) as GameObject;

            //剔除最上面的scene 物体
            if (go == null)
                return;

            DrawOutLine(go, selectionRect); // 绘制 GameObject 的外边框
            ShowLayerInfo(go, selectionRect); // 显示 Layer 信息
            ShowActiveToggle(go, selectionRect); // 显示 激活状态切换
            ShowComponentIcons(go, selectionRect); // 显示 组件图标
            DrawMarker(go, selectionRect); // 绘制标记

            // 只有在 Hierarchy 窗口聚焦时才处理键盘事件
            if (EditorWindow.focusedWindow != null && EditorWindow.focusedWindow.titleContent.text == "Hierarchy")
            {
                Event currentEvent = Event.current;

                // 检查是否按下了Ctrl+M
                if (currentEvent.type == EventType.KeyDown && currentEvent.keyCode == KeyCode.M && currentEvent.modifiers == EventModifiers.Control)
                {
                    // 检查当前选择的 GameObject
                    foreach (GameObject obj in Selection.gameObjects)
                    {
                        int id = obj.GetInstanceID();
                        if (markedObjects.ContainsKey(id))
                        {
                            markedObjects[id] = !markedObjects[id]; // 切换标记状态
                        }
                        else
                        {
                            markedObjects.Add(id, true); // 新增标记
                        }
                    }

                    // 重新绘制 Hierarchy 窗口
                    EditorApplication.RepaintHierarchyWindow();

                    Event.current.Use();
                }
            }
        }

        // 新增：根据标记状态绘制标记
        static void DrawMarker(GameObject go, Rect rect)
        {
            int instanceID = go.GetInstanceID();
            if (markedObjects.ContainsKey(instanceID) && markedObjects[instanceID])
            {
                rect.x = rect.x - 27; // 调整标记位置
                rect.width = 20;

                GUIStyle style = new GUIStyle();
                style.normal.textColor = Color.yellow;
                GUI.Label(rect, "★", style); // 使用星星符号作为标记，可以替换为其他符号
            }
        }

        // 绘制 GameObject 的外边框
        static void DrawOutLine(GameObject go, Rect rect)
        {
            if (!go.activeInHierarchy)
                return;

            GUI.Box(new Rect(rect.x - 16f, rect.y, rect.width, 2), "");
            GUI.Box(new Rect(rect.x - 16f, rect.y + rect.height, rect.width, 2), "");
            GUI.Box(new Rect(rect.x - 16f, rect.y, 2, rect.height), "");
        }

        // 显示 Layer 信息
        static void ShowLayerInfo(GameObject go, Rect rect)
        {
            if (rect.width < 280)
                return;

            rect.x = rect.width + rect.x - 120;
            rect.y += 2;

            var myStyle = new GUIStyle { normal = { textColor = Color.gray } };
            string layerName = LayerMask.LayerToName(go.layer);

            if (layerName != "Default")
            {
                GUI.Label(rect, '[' + layerName + ']', myStyle);
            }
        }

        // 显示 激活状态切换
        static void ShowActiveToggle(GameObject go, Rect rect)
        {
            rect.x = rect.width + rect.x - 15;
            rect.y += 2;

            bool originalActive = go.activeSelf;
            bool newActive = GUI.Toggle(rect, originalActive, string.Empty);

            if (originalActive != newActive)
            {
                Undo.RecordObject(go, "Toggle Active State");
                go.SetActive(newActive);
            }
        }

        // 显示 组件图标
        static void ShowComponentIcons(GameObject go, Rect rect)
        {
            //求出当前Hierarchy的总宽度
            rect.width += rect.x;
            //取得当前物体所有的 Component
            List<Component> components = new List<Component>(go.GetComponents<Component>());

            //遍历coms中的组件类型，如果有不显示的，就移除掉
            for (int i = 0; i < components.Count; i++)
            {
                if (!components[i])
                {
                    continue;
                }
                //剔除不需要显示的 组件
                if (TypeCheck(components[i].GetType()))
                {
                    components.RemoveAt(i);
                    i--;
                }
            }
            //图标的大小
            int iconSize = 16;

            //如果当前物体是 需要偏移的物体  offset 为 offsetNum 否则为 0
            int offset = go.transform == curOffsetTF ? offset = offsetCount : 0;

            //如果当前物体的组件数量 <=  maxIconNum  把nowOffset 和 offsetNum 
            if (components.Count <= MaxIconCount && go.transform == curOffsetTF)
            {
                curOffsetTF = null;
                offsetCount = 0;
            }

            if (components.Count > MaxIconCount)
            {
                GUIStyle style = new GUIStyle(GUI.skin.label);
                style.fontSize = 9;
                style.alignment = TextAnchor.MiddleCenter;

                if (GUI.Button(new Rect(rect.width - (iconSize + 1.5f) * (MaxIconCount + 2), rect.y + 1, 17, 17), "•••", style))
                {
                    if (curOffsetTF != go.transform)
                    {
                        curOffsetTF = go.transform;
                        offsetCount = 0;
                    }
                    offsetCount += MaxIconCount;
                    if (offsetCount >= components.Count)
                    {
                        offsetCount = 0;
                    }
                }
            }

            for (int i = 0; i + offset < components.Count && i < MaxIconCount; i++)
            {
                Component com = components[i + offset];
                PropertyInfo propertyInfo;
                try
                {
                    propertyInfo = com.GetType().GetProperty("enabled");
                }
                catch
                {
                    continue;
                }

                if ((bool)propertyInfo.GetGetMethod().Invoke(com, null))
                {
                    GUI.color = Color.white;
                }
                else
                {
                    GUI.color = Color.gray;
                }

                //根据组件类型返回 组件的小图标
                Texture2D comTexture = AssetPreview.GetMiniThumbnail(com);

                if (comTexture)
                {
                    GUIStyle style = new GUIStyle();
                    style.normal.textColor = Color.white;

                    if (GUI.Button(new Rect(rect.width - (iconSize + 1) * (i + 2), rect.y + 1, iconSize, iconSize), comTexture, style))
                    {
                        bool enabled = (bool)propertyInfo.GetGetMethod().Invoke(com, null);

                        Undo.RecordObject(com, enabled ? "Disable Component" : "Enable Component");
                        propertyInfo.GetSetMethod().Invoke(com, new object[] { !enabled });
                    }
                    GUI.color = Color.white;
                }
            }
        }

        // 检查当前组件类型 是否是 不显示类型
        static bool TypeCheck(Type type)
        {
            for (int i = 0; i < showTypes.Count; i++)
            {
                if (type == showTypes[i] || type.IsSubclassOf(showTypes[i]))
                {
                    return false;
                }
            }
            return true;
        }
    }
}